package tools

import (
	"bufio"
	"bytes"
	"fmt"
	"gitee.com/lllgo/gopkg/template"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

// 服务器信息
type Host struct {
	// 服务器名字
	Name string
	// 服务器分组
	Group []string
	// 服务器描述信息
	Desc map[string]interface{}
	// 服务器变量
	Vars map[string]string
}

const (
	ipKey = "ansible_ssh_host"
)

// 解析服务器配置文件获取服务器配置信息
func Parse(filename string) (hosts map[string]Host, err error) {
	f, err := os.Open(filename)
	defer f.Close()
	if err != nil {
		return hosts, err
	}

	hosts = make(map[string]Host, 0)
	lastGroup := ""
	reader := bufio.NewReader(f)
	for {
		line, _, err := reader.ReadLine()
		if err != nil {
			break
		}
		group, hs := parseLine(lastGroup, string(line))
		lastGroup = group
		for _, h := range hs {
			hosts[h.Name] = h
		}
	}

	return hosts, nil
}

// 解析服务器配置内容获取服务器配置信息
func ParseContent(content string) (hosts map[string]Host, err error) {
	hosts = make(map[string]Host, 0)
	lastGroup := ""

	for _, line := range strings.Split(content, "\n") {
		group, hs := parseLine(lastGroup, line)
		lastGroup = group
		for _, h := range hs {
			hosts[h.Name] = h
		}
	}
	return hosts, nil
}

var (
	nameSeqReg, _   = regexp.Compile(`.*\[[\d]+:[\d]+\].*`)
	ipSeqReg, _     = regexp.Compile(`.*\[[\d]+\].*`)
	ipAddrSeqReg, _ = regexp.Compile(`\[.*,.*\]`)
)

// 解析服务器配置行
func parseLine(lastGroup, line string) (group string, hosts []Host) {
	group = lastGroup

	lineStr := strings.TrimLeftFunc(line, unicode.IsSpace)

	if lineStr == "" {
		// 遇到空行或忽略行重置分组
		group = ""
		return
	}

	// ignore comment
	if lineStr[0:1] == "#" {
		return
	}

	//只有#开头的才认为是注释
	//if i := strings.Index(lineStr, "#"); i == -1 {
	lineStr = strings.TrimRightFunc(lineStr, unicode.IsSpace)
	//} else {
	//	lineStr = strings.TrimRightFunc(lineStr[0:i], unicode.IsSpace)
	//}

	lenLineStr := len(lineStr)
	if lineStr[0:1] == "[" && lineStr[lenLineStr-1:lenLineStr] == "]" {
		group = lineStr[1 : lenLineStr-1]
	} else {
		firstSpaceIndex := strings.Index(lineStr, " ")
		name := lineStr[0:firstSpaceIndex]

		if nameSeqReg.MatchString(name) {
			arr := strings.Split(name, ":")
			start, _ := strconv.Atoi(arr[0][strings.LastIndex(arr[0], "[")+1:])
			end, _ := strconv.Atoi(arr[1][0:strings.Index(arr[1], "]")])
			namePre, nameSuf := name[0:strings.Index(name, "[")], name[strings.Index(name, "]")+1:]

			for i := 0; i < end-start+1; i++ {
				desc := map[string]interface{}{}
				for _, str := range strings.Split(lineStr[firstSpaceIndex+1:], " ") {
					arr := strings.Split(str, "=")
					if len(arr) == 2 {
						if arr[0] == ipKey {
							ip := arr[1]
							if ipAddrSeqReg.MatchString(ip) {
								desc[ipKey] = strings.Split(ip[1:len(ip)-1], ",")[i]
							} else if ipSeqReg.MatchString(ip) {
								ipPre, ipSuf := ip[0:strings.Index(ip, "[")], ip[strings.Index(ip, "]")+1:]
								start, _ := strconv.Atoi(ip[strings.Index(ip, "[")+1 : strings.Index(ip, "]")])
								desc[ipKey] = ipPre + strconv.Itoa(start+i) + ipSuf
							} else {
								ipPre := ip[0 : strings.LastIndex(ip, ".")+1]
								num, _ := strconv.Atoi(ip[strings.LastIndex(ip, ".")+1:])
								desc[ipKey] = ipPre + strconv.Itoa(num+i)
							}
						} else {
							desc[arr[0]] = arr[1]
						}
					} else {
						continue
					}
				}
				hosts = append(hosts, Host{
					Name: namePre + strconv.Itoa(start+i) + nameSuf,
					Desc: desc,
				}.AddGroup(group))
			}
		} else {
			desc := map[string]interface{}{}
			for _, str := range strings.Split(lineStr[firstSpaceIndex+1:], " ") {
				arr := strings.Split(str, "=")
				if len(arr) == 2 {
					desc[arr[0]] = arr[1]
				} else {
					continue
				}
			}
			hosts = append(hosts, Host{
				Name: name,
				Desc: desc,
			}.AddGroup(group))
		}
	}

	return
}

func (h Host) AddDesc(key string, val interface{}) Host {
	if _, ok := h.Desc[key]; !ok {
		h.Desc[key] = val
	}
	return h
}

func (h Host) AddGroup(group string) Host {
	if group != "" {
		add := true
		if h.Group != nil {
			for _, g := range h.Group {
				if g == group {
					add = false
					break
				}
			}

		}
		if add {
			h.Group = append(h.Group, group)
		}
	}

	return h
}

func (h Host) Line() string {
	line := ""
	ip := h.IP()
	for k, v := range h.Desc {
		if k != ipKey {
			line += " " + k + "=" + v.(string)
		}
	}
	return ip + line
}

func (h Host) IP() string {
	if tmp, ok := h.Desc[ipKey]; ok {
		return tmp.(string)
	}
	panic(fmt.Sprintf("未设置IP %+v", h))
}

func JoinIP(hs []Host, sep string) string {
	return Join(hs, sep, func(h Host) string {
		return h.IP()
	})
}

func Join(hs []Host, sep string, f func(h Host) string) string {
	sb := bytes.NewBufferString("")
	for _, h := range hs {
		sb.WriteString(sep + f(h))
	}
	tmp := sb.String()
	if tmp == "" {
		return tmp
	}
	return tmp[1:]
}

//TODO 增加toYaml
var (
	templateHostKey = "host"
)

func TemplateFuncHost(names []string) (string, error) {
	str := ""
	for _, s := range names {
		host := template.GetMetaData(templateHostKey).(map[string]Host)[s]
		if host.Name != "" {
			str += host.Line() + "\n"
		} else {
			str += s + " no host info\n"
		}

	}
	return str, nil
}

func TemplateFuncHostIp(name string) (string, error) {
	return template.GetMetaData(templateHostKey).(map[string]Host)[name].IP(), nil
}
